/**
    \file tp4.cpp

    \brief Programme d'aide au pilote.

    \author   Clement Zotti 11074801
    \author   Ousmane Iria 10100204
    \date     11/03/2012
    \version  v1.0

    <b>Entrees:</b>
        \li (\c clavier) coordonnées du pilote  (\c chaine de caractères)
        \li (\c clavier) action à effectue  (\c chaine de caractères)
        \li (\c fichier) Fichier de coordonnées  (\c chaines de caractères)

    <b>Sorties:</b>
        \li (\c écran) liste des aéroports ainsi que de leurs coordonnées (\c chaine de caractères)

    Ce programme aide le pilote pour le choix d'un aéroport, selon
    la méthode de tri choisie. Dans un premier temps, le pilote
    entre ses coordonnées GPS dans le programme. Ensuite il choisie une action
    entre ("alpha.", "distance.", "suivant.", "fin."), en fonction de ce choix
    soit le programme tri en fonction des coordonnées, soit en fonction des
    distance le séparant des aéroports. Ou il peut a nouveau rentrer ses
    nouvelles coordonnées et enfin il peut aussi mettre fin au programme.

    Date de la premiere version: 06/03/2012 \n

    Date de modification:
	\li 19/03/2012, ajout des fonctions de calcul et de conversion avec correction des tris, Ousmane Iria
        \li 11/03/2012, modification des fonctions et ajout des commentaires, Clement Zotti
*/

#define MAX_AEROPORT    50  // Nombre maximun d'aéroports
#define MAX_CARACTERES  20  // Nombre maximum de caractères pour le nom d'un aeroports
#define NB_COORD        3   // Nombre de coordonnées, longitude, latitude, distance
#define DEGRE           0   // index pour les coordonnées DEGRE
#define MINUTE          1   // index pour les coordonnées MINUTE
#define SECONDE         2   // index pour les coordonnées SECONDE
#define DIST            2   // index pour les coordonnées DISTANCE
#define RADIAN_I	3   // index des radians dans les coordonnées du pilote
#define LAT		0   // index des latitudes dans le tableau coordonnées
#define LONGI		1   // index des longitudes dans le tableau coordonnées

const char SEP_ACTION = '.';

/*-- Bibliotheques utilisees en ordre alphabetique --*/
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>


using namespace std;

/**
    \brief Description sommaire de la fonction (i.e. du programme)
  */

int main(void)
{
    // Déclaration des focntions;
    int Traitement_fichier(char[][MAX_CARACTERES], float[][NB_COORD]);
    int Traitement_donnees(char[][MAX_CARACTERES], float[][NB_COORD], int);
    int init_tab(char[][MAX_CARACTERES], int, int);
    int init_tab(float[][NB_COORD], int, int);

    // Déclaration des variables
    char nom[MAX_AEROPORT][MAX_CARACTERES];
    float coord[MAX_AEROPORT][NB_COORD];

    int nb_aero;

    // Boucle principale

    // Initialisation des valeurs des matrices
    init_tab(nom,MAX_AEROPORT,MAX_CARACTERES);
    init_tab(coord,MAX_AEROPORT,NB_COORD);


    nb_aero = Traitement_fichier(nom, coord);
    Traitement_donnees(nom, coord, nb_aero);

    // retour
    return 0;
}

/**
  \brief Initialisation d'une matrice de réel.

  Cette fonction initialise une matrice de d'entier avec des 0.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in,out] tab (Matrice a initialiser)
  \param[in] nb_ligne (Nombre de ligne du tableau)
  \param[in] nb_colonne (Nombre de colonne du tableau)

  \return CODE
*/
int init_tab(float tab[][NB_COORD], int nb_ligne, int nb_colonne)
{
    for (int i=0 ; i < nb_ligne ; i++)
        for (int j = 0 ; j < nb_colonne ; j++)
            tab[i][j] = 0;
    return 0;
}

/**
  \brief Initialisation d'une matrice de caractère.

  Cette fonction initialise une matrice de caractère avec des 0.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in,out] tab (Matrice a initialiser)
  \param[in] nb_ligne (Nombre de ligne du tableau)
  \param[in] nb_colonne (Nombre de colonne du tableau)

  \return CODE
 **/
int init_tab(char tab[][MAX_CARACTERES], int nb_ligne, int nb_colonne)
{
    for (int i=0 ; i < nb_ligne ; i++)
        for (int j = 0 ; j < nb_colonne ; j++)
            tab[i][j] = 0;
    return 0;
}


/**
  \brief Initialisation d'une matrice de caractère.

  Cette fonction initialise une matrice de caractère avec des 0;

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in,out] tab (Tableau a initialiser)
  \param[in] nb_ligne (Nombre de ligne du tableau)

  \return CODE
 **/
int init_tab(int tab[], int nb_ligne)
{
    for (int i=0 ; i < nb_ligne ; i++)
        tab[i] = 0;
    return 0;
}


/**
  \brief Ouverture du fichier pour traitement.

  Cette fonction ouvre un fichier dont le nom est entré au clavier
  et repartit les elements du fichier dans deux tableaux.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in,out] nom (Matrice de caractères pour le nom des aéroports)
  \param[in,out] coord (Matrice de coordonnées)

  \return le nombre d'aéroports présent dans le fichier.
 **/
int Traitement_fichier (char nom[][MAX_CARACTERES], float coord[][NB_COORD])
{
    // Declaration des fonctions utilisees dans la fonction
    float Conversion_Radian_R (float);

    // Declaration des constantes
    const int VAL_0 = 48;	/// Valeur du 0 dans la table ASCII 48

    // Declaration des variables locales
    bool	isCoord = false;
    bool	fOK = false;
    bool	isLat = true;
    char	nom_fichier[MAX_CARACTERES];
    char	conv[MAX_CARACTERES];

    int	cpt_aero = 0;
    int	cpt_coord = 0;
    float tmp_coor;
    ifstream fAeroport;

    // Corps de la fonction
    do
    {
        cout << "Entrez le nom du fichier qui contient les aeroports :";
        cin.getline(nom_fichier, MAX_CARACTERES);
        fAeroport.open(nom_fichier, ifstream::in);
        if (!fAeroport)
        {
            fAeroport.clear();
            cout << "Erreur a l'ouverture." << endl;
        } else
            fOK = true;
    } while (!fOK);

    // AJOUTER UN TEST DE ROBUSTESSE SUR LE FICHIER POUR VÉRIFIER QUE
    // SONT CONTENU SOIT CONFORME A CELUI QU'IL FAUT.

    while (!fAeroport.eof())
    {
        fAeroport.getline(conv, MAX_CARACTERES);
        if (!isCoord)
        {
            {
                int i = 0;
                while (conv[i] != '.' && conv[i] != '\n')
                {
                    nom[cpt_aero][i] = conv[i];
                    i++;
                }
                nom[cpt_aero][i] = '.';
            }
            isCoord = true;
            cpt_coord = 0;
        }
        else
        {
            // Fonction qui converti un tableau de caractère en entier.
            tmp_coor = atoi(conv);
            tmp_coor /= pow(60.0,cpt_coord);

            if (cpt_coord < 2 && isLat)
            {
                coord[cpt_aero][LAT] += tmp_coor;
                cpt_coord++;
            }
            else if (cpt_coord == 2 && isLat)
            {
                coord[cpt_aero][LAT] += tmp_coor;
                cpt_coord = 0;
                isLat = false;
            }
            else if (cpt_coord < 2 && !isLat)
            {
                coord[cpt_aero][LONGI] += tmp_coor;
                cpt_coord++;
            }
            else if (cpt_coord == 2 && !isLat)
            {
                coord[cpt_aero][LONGI] += tmp_coor;
                cpt_aero++;
                isCoord = false;
                isLat = true;
            }
        }
    } // Fin lecture du fichier
    fAeroport.close();

    for (int i = 0 ; i < cpt_aero ; i++)
    {
        coord[i][LAT] = Conversion_Radian_R(coord[i][LAT]);
        coord[i][LONGI] = Conversion_Radian_R(coord[i][LONGI]);
    }

    return cpt_aero;
    // ne pas oublier le retour
}

/**
  \brief Traite les données en fonctions des actions.

  Lit les coordonnées du pilote au clavier, les actions
  et effectue l'action souhaité.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in] nom (Matrice des noms d'aéroports)
  \param[in] coord (Matrice de coordonnées des aéroports)
  \param[in] nb_aero (Nombre d'aéroport lu dans le fichier)

 **/
void Traitement_donnees (char nom[][MAX_CARACTERES],
                         float coord[][NB_COORD],
                         int nb_aero)
{
    // Declaration des fonctions utilisees dans le main (a partir du ch.3)
    void coordonnees_pilote(float[], float[]);
    bool action_Valide(char[], int&);
    float Calcul_distance (float, float, float, float);
    float Conversion_Radian_R (float);
	void affiche_matrice(const char[][MAX_CARACTERES], const float[][NB_COORD], int);
	int tri_selectif_reel_r_dual(float[][NB_COORD],
								   char[][MAX_CARACTERES],
								   int );
	int tri_selectif_carac_r_dual(char[][MAX_CARACTERES],
								  float[][NB_COORD],
								  int max);

    // Declaration des constantes
    const int ERREUR_ACTION = -1;
    const int SUIVANT = 1;
    const int FIN = 2;
    const int ALPHA = 3;
    const int DISTANCE = 4;

    // Declaration des variables locales
    char action[MAX_CARACTERES];

    float latitude_pilote[4], longitude_pilote[4];

    int code_action;

    bool estValide = false;

    // Corps de main : enonce executable

    //Boucle principale
    do
    {
        // cin et cout des coordonnées du pilote
        coordonnees_pilote(latitude_pilote, longitude_pilote);

        // Demande du choix de tri
        do
        {
            // Demande l'action à éffectué
            do
            {
                cout << " Entrez votre choix d'action (suivant., fin., alpha., distance.) : "
                     << endl;
                cin.getline(action, MAX_CARACTERES);
            }while (!action_Valide(action, code_action));

            if (code_action == DISTANCE || code_action == ALPHA)
            {
                // CALCUL SUR LA MATRICE DES COORDONNÉES
                // conversion
                for (int i = 0; i< nb_aero; i++)
                {
                    // distance
                    coord[i][DIST] = Calcul_distance(latitude_pilote[RADIAN_I],
                                                     longitude_pilote[RADIAN_I],
                                                     coord[i][LAT],
                                                     coord[i][LONGI]);
                }

                if (code_action == ALPHA)
                    tri_selectif_carac_r_dual(nom, coord, nb_aero);
                else if (code_action == DISTANCE)
                    tri_selectif_reel_r_dual(coord, nom, nb_aero);

                // Affichage des matrices selon le tri
				affiche_matrice(nom, coord, nb_aero);
            }

        } while (code_action != FIN && code_action != SUIVANT); // fin do/while tri

    } while (code_action != FIN); // fin do/while coordonnées pilote
}

/**
  \brief Fonction d'affichage des matrices.

  Cette fonction s'occupe d'afficher les matrices passées en paramètres

  Cette fonction est correcte.
  Cette fonction est robuste.

  \param[in,out] action (Tableau de caractères contenant l'action)
  \param[in,out] act (Entier représentant le type d'action entrée)

  \return validité de l'action
*/
void affiche_matrice(const char nom[][MAX_CARACTERES],const float coord[][NB_COORD], int nb_aero)
{
	// Boucle principale
	for (int i =0; i< nb_aero; i++)
	{
		cout << nom[i] << endl;
		cout << "Latitude : " << coord[i][LAT] << endl;
		cout << "Longitude : " << coord[i][LONGI] << endl;
		cout << "Separation : " << coord[i][DIST] << endl;
		cout << endl;
	}
}


/**
  \brief Vérification d'une action valide.

  Cette fonction vérifie que les actions entrées au clavier sont valide

  Cette fonction est correcte.
  Cette fonction est robuste.

  \param[in,out] action (Tableau de caractères contenant l'action)
  \param[in,out] act (Entier représentant le type d'action entrée)

  \return validité de l'action
*/
void coordonnees_pilote(float latitude_pilote[], float longitude_pilote[])
{
    //Déclaration des fonctions
    float Conversion_Radian_R(float);

    // Programme principal
    do
    {
        cout << "Entrez votre latitude (deg min sec) : " << endl;
        cin >> latitude_pilote[DEGRE] >> latitude_pilote[MINUTE] >> latitude_pilote[SECONDE];
    } while (latitude_pilote[DEGRE] < 0 || latitude_pilote[MINUTE] < 0 || latitude_pilote[SECONDE] < 0);

    do
    {
        cout << "Entrez votre longitude (deg min sec) : " << endl;
        cin >> longitude_pilote[DEGRE] >> longitude_pilote[MINUTE] >> longitude_pilote[SECONDE];
    } while (longitude_pilote[DEGRE] < 0 || longitude_pilote[MINUTE] < 0 || longitude_pilote[SECONDE] < 0);

    latitude_pilote[DEGRE] += (latitude_pilote[MINUTE]/60) + (latitude_pilote[SECONDE]/3600);
    longitude_pilote[DEGRE] += (longitude_pilote[MINUTE]/60) + (longitude_pilote[SECONDE]/3600);

    latitude_pilote[RADIAN_I] = Conversion_Radian_R(latitude_pilote[DEGRE]);
    longitude_pilote[RADIAN_I] = Conversion_Radian_R(longitude_pilote[DEGRE]);
}


/**
  \brief Vérification d'une action valide.

  Cette fonction vérifie que les actions entrées au clavier sont valide

  Cette fonction est correcte.
  Cette fonction est robuste.

  \param[in,out] action (Tableau de caractères contenant l'action)
  \param[in,out] act (Entier représentant le type d'action entrée)

  \return validité de l'action
*/
bool action_Valide(char action[], int &act)
{
    // Déclaration des fonctions
    bool str_compare(const char[],const char[], char);

    // Déclarations constantes
    const int MAX_CHAR = 10;
    const char SUIVANT[MAX_CHAR] = "suivant.";
    const char FIN[MAX_CHAR] = "fin.";
    const char ALPHA[MAX_CHAR] = "alpha.";
    const char DISTANCE[MAX_CHAR] = "distance.";

    // Déclaration variables
    bool estValide = false;

    // Corps de la fonction

    if (action[0] == SUIVANT[0])
    {
        if (str_compare(action, SUIVANT, '.'))
        {
            estValide = true;
            act = 1;
        }
    }
    else if (action[0] == FIN[0])
    {
        if (str_compare(action, FIN, '.'))
        {
            estValide = true;
            act = 2;
        }
    }
    else if (action[0] == ALPHA[0])
    {
        if (str_compare(action, ALPHA, '.'))
        {
            estValide = true;
            act = 3;
        }
    }
    else if (action[0] == DISTANCE[0])
    {
        if (str_compare(action, DISTANCE, '.'))
        {
            estValide = true;
            act = 4;
        }
    }
    return estValide;
}


/**
  \brief Comparaison de deux chaines de caractère

  Cette fonction compare 2 chaines de caractères passées en paramètres
  avec un caractère de fin de chaine. Et retourne si les chaines sont les mêmes.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \param[in] str (Tableau contenant la chaine à comparer)
  \param[in] str_cmp (Tableau contenant la chaine comparant)
  \param[in] fin_char (Caractères de fin)

  \pre le caractère de fin de chaine doit être présent dans au moins une des deux chaines.

  \return booléen
*/
bool str_compare(const char str[],const char str_cmp[], char fin_char)
{
    // Corps de la fonction
    for (int i = 0; str[i] != fin_char || str_cmp[i] != fin_char; i++)
    {
        if (str[i] != str_cmp[i])
            return false;
    }
    return true;
}



/**
  \brief Calcul de la distance entre 2 coordonnées.

  Ce module calcul la distance orthodromique entre deux points
  sur le globe terrestre.

  Cette fonction est correcte.

  \param[in,out] latitude (Lit la latitude)
  \param[in,out] longitude (Lit la longitude)

 **/
float Calcul_distance (float latitude_pilote,
                       float longitude_pilote,
                       float latitude_avion,
                       float longitude_avion)
{
    // Déclaration des constantes
    const int RAYON_TERRE = 6378;

    //Déclaration variables
    float distance_ortho;

    // Corps de la fonction

    distance_ortho = RAYON_TERRE * acos((sin(latitude_pilote) *
                                         sin(latitude_avion)) +
                                        (cos(latitude_pilote) * cos(latitude_avion) *
                                         cos(longitude_avion - longitude_pilote)));

    return distance_ortho;
}


/**
  \brief Robustesse de Conversion_Radian.

  Ce module ajoute la robustesse à la fonction
  Conversion_Radian.

  Cette fonction est correcte.
  Cette fonction est robuste.

  \param[in] degree

  \return radian
 **/
float Conversion_Radian_R (float deg)
{
    //Déclaration de fonction
    float Conversion_Radian(float);
    // Corps de la fonction
    if (deg < 0 && deg > 360)
        return -1;
    return Conversion_Radian(deg);
}

/**
  \brief Calcul de la distance entre 2 coordonnées.

  Ce module calcul la distance orthodromique entre deux points
  sur le globe terrestre.

  Cette fonction est correcte.
  Cette fonction n'est pas robuste.

  \pre Les degrées doivent être entre 0 et 360.

  \param[in] degree

  \return radian
 **/
float Conversion_Radian (float deg)
{
    //Déclaration constantes
    const float PI = 3.14159265;
    const int RADIAN = 180;

    // Déclaration des variables
    float total_radian;

    // Corps de la fonction    float total_degre, total_radian;
    total_radian = deg*PI/RADIAN;

    return total_radian;
}

/**
	\brief fonction de tri_selectif pour des entiers. Récursif !

	Cette fonction tri une matrice par rapport au valeur entière
	de sa troisième colonne, elle tri aussi un tableau de nom
	par rapport au tri des entiers.
	
	Cette fonction est correcte.
	Cette fonction n'est pas robuste.

	\param[in,out] ia (sous tableau contenant les premières coordonnées à echanger)
	\param[in,out] ib (sous tableau contenant les secondes coordonnées à echanger)

*/
void swap_t_reel(float ia[], float ib[])
{
    float tmpi;

    for (int i = 0; i < NB_COORD; i++)
    {
        tmpi = ib[i];
        ib[i] = ia[i];
        ia[i] = tmpi;
    }
}

/**
	\brief fonction d'échange pour des caractères.

	Cette fonction échange deux chaines de caractères se terminant par un "."
		
	Cette fonction est correcte.
	Cette fonction n'est pas robuste.

	\param[in,out] ca (sous tableau contenant le nom à échanger)
	\param[in,out] cb (sous tableau contenant le nom à échanger)

*/
void swap_t_car( char ca[], char cb[])
{
    char tmpc;
    int indexmax, cpt;

    cpt=0;
	while (ca[cpt] != SEP_ACTION) cpt++;
	indexmax = cpt;
	
    cpt=0;
    while (cb[cpt] != SEP_ACTION)cpt++;
	if (cpt>indexmax)
		indexmax = cpt;

    for (int i = 0 ; i <= indexmax; i++)
    {
        tmpc = ca[i];
        ca[i] = cb[i];
        cb[i] = tmpc;
    }
}


/**
	\brief fonction de tri_selectif pour des entiers. Récursif !

	Cette fonction tri une matrice par rapport au valeur entière
	de sa troisième colonne, elle tri aussi un tableau de nom
	par rapport au tri des entiers.
	
	Cette fonction est correcte.
	Cette fonction n'est pas robuste.

	\param[in,out] tab (matrice contenant les réels à trier)
	\param[in,out] tab_suiv (matrice de caractères trier à partir de l'autre matrice)
	\param[in] index maximal des tableaux

	\return code d'erreur ou succes.
*/
int tri_selectif_reel_r_dual(float coord[MAX_AEROPORT][NB_COORD],
                               char nom[MAX_AEROPORT][MAX_CARACTERES],
                               int max)
{
    // Déclaration des fonctions
    void swap_t_reel(float[], float[]);
    void swap_t_car(char[], char[]);

    // Déclaration des variables
    int index = 0;
    float tmp = 0;
    if (max > 1)
    {
        for (int i=0; i < max; i++)
        {
            if (tmp < coord[i][DIST])
            {
                index = i;
                tmp = coord[i][DIST];
            }
        }

        swap_t_reel(coord[index], coord[max-1]);
        swap_t_car(nom[index], nom[max-1]);

        tri_selectif_reel_r_dual(coord, nom, max-1);
    }
    return 0;
}

/**
	\brief fonction de tri_selectif pour des caractères. Récursif !
1
	Cette fonction tri une matrice par rapport aux valeurs des
	caractères qui la compose, elle tri aussi un tableau de nom
	par rapport au tri des entiers.
	
	Cette fonction est correcte.
	Cette fonction n'est pas robuste.

	\param[in,out] tab (matrice de caractères à trier)
	\param[in,out] tab_suiv (matrice contenant les réels trier à partir de l'autre matrice)
	\param[in] index maximal des matrice

	\return code d'erreur ou succes.
*/
int tri_selectif_carac_r_dual(char tab[MAX_AEROPORT][MAX_CARACTERES],
                              float tab_suiv[MAX_AEROPORT][NB_COORD],
                              int max)
{
    // Déclaration des fonctions

    // Déclaration des variables
    int index = 0;

    if (max > 1)
    {
        for (int i=0; i < max; i++)
        {
            for (int j = 0; j < MAX_CARACTERES; j++)
            {
                if (tab[index][j] < tab[i][j])
                {
                    index = i;
                    j=MAX_CARACTERES;
                } else if (tab[index][j] > tab[i][j])
                    j=MAX_CARACTERES;
            }
        }

        swap_t_car(tab[index], tab[max-1]);
        swap_t_reel(tab_suiv[index], tab_suiv[max-1]);

        tri_selectif_carac_r_dual(tab, tab_suiv, max-1);
    }
    return 0;
}










